A Concise Introduction to Scala
Copyright © 2011, 2013 David Matuszek
In a Java application, you need a
method. In a Scala application, you need a
method.
How to run | Code | Results/Comments |
---|---|---|
On the command line as a REPL (Read-Eval-Print-Loop). | dave% scala |
No
|
As a script. | println("Hello from a script.") |
dave% scala HelloScript.scalaScala automatically wraps your lines in a class and a main method, so don't do this yourself. |
As a shell script. | #!/usr/local/share/bin/scala println("Hello from a script.") |
This is the correct location for Scala on my Mac, but it doesn't work for me. (Should try it on UNIX and Windows). |
As an application. | object HelloApp extends App { println("Hello, World!") } |
dave% scalac hello.scala dave% scala HelloApp Hello, World!File name doesn't have to be the same as the application name. The App trait wraps your code in a main method. |
As a single object. | object HelloObject { println("In object") def main(args: Array[String]) { println("In main") } } |
dave% scalac HelloProgram.scala dave% scala HelloObject In object In main |
As a program. | class HelloClass { def inClass() { println("In class") } } object HelloObjectPlusClass { println("In object") def main(args: Array[String]) { println("In main") val c = new HelloClass c.inClass() } } |
dave% scalac HelloWithClass.scala dave% scala HelloObjectPlusClass In object In main In class The object may not have the same name as the class!
|
Scala has four types of identifiers:
$
' counts as a letter, but is reserved for use by Scala.val
s) are
camel case and begin with a capital.abc123
, myVar
, myVal
, myMethod
,
MyClass
, MyObject
, Pi
, E
.
( ) [ ] { } ' " _ . , ; , `
:
) are right-associative, all others are left-associative.+ => <?> :::
unary_+
, myVar_=
`
character).`Hello, World!`
, `class`
All variables must be declared, with either var
(if the value may change) or val
(for constants). In Scala, val
is preferred, unless you have good reason to use var
.
If given an initial value in the declaration, the variable's type is
inferred and need not be explicitly stated (but it may be). If
explicitly stated, the type comes after the variable and a colon, for
example, var q: Boolean = true
.
The types of variables must be declared:
Type parameters (generics) are enclosed in square brackets, for example, Array[Int]
.
Within a method, variables must be given an initial value. Variables within a class (and not within a method) may optionally be given an initial value.
When initial values are not given, new
is required: val ary = new Array[Int](5)
and default values of zero, false
, or null
are used.
When initial values are given,
new
is not allowed: val ary2 = Array(3, 1, 4, 1, 6)
The type of a function is written with the double-headed arrow, =>
or the Unicode version, ⇒
. Except in the case of a single argument, parentheses are required:
() => return_type
arg_type => return_type
or (arg_type) => return_type
(arg_type_1, ..., arg_type_N) => return_type
Scala has no primitives, only objects.
Any
AnyVal
Boolean
, Char
, Byte
, Short
, Int
, Long
, Float
, Double
(these are as in Java)Unit
(has only a single value, ()
; returned by functions that “don't return anything.”)AnyRef
(corresponds to Object
in Java)
ScalaObject
scala.*
reference types, including Array
and List
Null
(bottom of all AnyRef
objects)Nothing
(bottom of Any
)Char
, Byte
, Short
, Int
, Long
, Float
, Double
are considered to be numeric types. The default type for integers is Int
, and the default type for "real" numbers is Double
. The less accurate forms are converted to Int
or Double
when arithmetic is performed using them.
All numbers are stored internally in a binary representation, but they may be written in various ways.
0
to 9
, with an optional +
or -
prefix. l
or L
suffix. Due to its resemblance to the digit 1
, use of the letter l
is strongly discouraged.0
to 7
, beginning with a 0
, and an optional +
or -
prefix.0
to 9
and the letters A
to F
(may be lowercase), and an optional +
or -
prefix.0
to 9
, with an optional +
or -
prefix and an optional exponent. The exponent consists of an e
or E
, an optional sign, and one to three decimal digits.
with an f
or F
suffix.Operations on numbers include +
(addition), -
(subtraction or negation), *
(multiplication), /
(division), and %
(modulus). Operations on integers also include <<
(left shift), >>
(right shift with sign extension), >>>
(right shift with zero fill), &
(bitwise and), |
(bitwise or), and ^
(bitwise exclusive or).
A String
may be enclosed in double quotes, "..."
, or in triple double quotes, """..."""
. The latter is a raw string (that is, \
does not "escape" characters) and may contain newlines.
A Symbol
is an interned string. A symbol literal starts with a single quote, '
, followed by a letter, then zero or more letters and digits.
Lists are the most valuable data type in any functional language, including Scala.
Much of the following information is from http://anders.janmyr.com/2009/10/lists-in-scala.html.
Lists are:
List[Nothing]
.When it is necessary to specify the type of a list, write it as List[Type]
. A literal list is written as List(value, ..., value)
.
list.head
returns the first element in the list.list.tail
returns the rest of the list, minus the head.value :: list
(::
is pronounced “cons”) puts the value as the new head of the list. It is right-associative.
List()
or as Nil
. List(1, 2, 3)
is equivalent to 1 :: 2 :: 3 :: Nil
.list.isEmpty
checks if the list is empty.list.length
returns the number of elements in the list.list.last
returns the last element of the list.list.init
returns a list with the last element removed.list.take(n)
returns a list of the first n elements. Especially useful for lazy lists.list.drop(n)
returns a list with the first n elements removed.list1 ::: list2
appends the two lists.list.reverse
returns a list in the reverse order.list.splitAt(n)
returns the tuple (list take n, list drop n)
.list1.zip(list2)
returns a list of tuples, where the first element of the tuple is taken from list1
and the second from list2
. The length of the result is the length of the shorter list.list.mkString(string)
converts each element of the list into a string, and concatenates them with the string
in between elements. listOfLists.flatten
takes a list of lists of elements and returns a list of elements.The following functions are described as operations on lists, but most of them will operate on many types of sequences.
list.map(function)
returns a list in which the function of one argument has been applied to each element.listOfLists.flatMap(function)
returns a list in which the function of one argument has been applied to
each element of each sublist. Removes one “level” of nesting.list.filter(predicate)
returns a list of the elements of the given list for which the predicate is true.list.filterNot(predicate)
returns a list of the elements of the given list for which the predicate is false.list.partition(predicate)
returns a tuple of two lists: a list of values that satisfy the predicate, and a list of those that do not.list.foldLeft(value)(_ function _)
applies the function to each pair of elements of value::list
, using each function result as the new first argument to the function, and returns the final value.
(value /: list)(_ function _)
is an alternate way of writing foldLeft
.list.foldRight(value)(_ function _)
applies the function to each pair of elements of the list with the
value appended, starting from the right end of the list, using each
function result as the new second argument to the function, and returns
the final value.(list :\ value)(_ function _)
is an alternate way of writing foldRight
.list.find(predicate)
returns, as Some(value)
, the first value in the list that satisfies the predicate, or None
if no such value is found.list.takeWhile(predicate)
returns a
list of the values at the front of the given list that satisfy the
predicate, stopping short of the first value that does not.list.dropWhile(predicate)
returns a list omitting those values at the front of the given list that satisfy the predicate.list.span(predicate)
returns the tuple (list takeWhile predicate, list dropWhile predicate)
.list.forall(predicate)
checks if every element of the list satisfies the predicate.list.exists(predicate)
checks if any element of the list satisfies the predicate.list.sortWith(comparisonFunction)
sorts a list using the two-parameter comparison function. list.sortWith(_ < _)
list.groupBy(function)
returns a Map of keys → values, where the keys are the results of applying the function to each list element, and the values are a List of values in list
such that applying the function to that value yields that key. For example, List("one", "two", "three").groupBy(x => x.length)
gives Map(5 -> List(three), 3 -> List(one, two))
.A tuple consists of from 2 to 22 comma-separated values enclosed in parentheses. Tuples are immutable. To access the n-th value in a tuple t
, use the notation t._
n
, where n
is a literal integer in the range 1 (not 0!) to 22. There may be a spaces around the dot, but not between _
and n
.
Sets are immutable by default. Mutable sets may be imported from scala.collection.mutable
. There are a great many operations on sets, of which contains
, isEmpty
, intersect
, union
, and diff
are just a few.
Maps are immutable by default. Mutable maps may be imported from scala.collection.mutable
. A map is represented as a list of pairs, that is,
. A more expressive syntax for writing the same thing is
.
The basic operations on maps are (1) getting the value associated with a key, and (2) for mutable maps, associating a value with a key. Most list operations can also be used on maps.
map.get(key)
returns (as an Option
) the value associated with the key.map.getOrElse(key, default)
returns the value associated with the key, or if the key is not in the map, the default value.map.put(key, value)
for mutable maps only, associates the value with the key, and returns (as an Option
) the old value. Options are used when an operation may or may not succeed in
returning a value. Options are parameterized types, so one may have, for
example, an Option[String]
type. The possible values are Some(value)
, where the value is of the correct type, or None
, for the case where no value has been found.
Although a few operations are defined for Option
types, it is far more common to use a match
expression to extract the value, if one exists.
Arrays are mutable. The advantage of arrays is fast access to random elements. Unless this feature is needed, consider using Lists instead.
An array may be created by listing its values:
val ary = Array(2, 3, 5, 7, 11)
or by explicitly giving its type, and using new to indicate its size:
al ary: Array[Int] = new Array(100)
The basic operations on arrays are getting a value from it, storing a value in it, and getting its length. Note the use of parentheses instead of brackets.
array(n)
returns the n
-th value (0-based) in the array.array(n) = value
puts the value
in the n
-th location (0-based) of the array. array.length
returns the number of elements in the array. To create a multidimensional array, import Array._
, then call
. The elements of a multidimensional array can be accessed using the syntax
.
if (condition) expression
if (condition) expression else expression
if (condition) expression else if (condition) expression ...
if (condition) expression else if (condition) expression ... else expression
The value of the if expression is the value of the expression that is chosen; or, if no condition is satisfied and there is no else
clause, the value is the Unit
value, ()
.
The for expression is used to create and return a sequence of results. The syntax is for (seq) yield expression
, where:
seq
is a semicolon-separated sequence of generators
, definitions
, and filters
, beginning with a generator.
generator
has the form variable <- list
, where the list may be any expression resulting in a list.definition
has the form variable = expression
(the keywords var
and val
are not used here).filter
has the form if condition
.seq
.Examples:
for (v <- list) yield v
returns a copy of list
.for (v <- list) yield f(v)
is equivalent to list map f
.for (v <- list if p(v)) yield v
is equivalent to list filter p
.for (v <- lst; w = 2 * v; x <- lst; if x < w) yield (v, w, x)
Syntax:
expression match { case pattern1 => expression1 case pattern2 => expression2 ... case patternN => expressionN }
The match
expression uses pattern matching to select a case, then the value of the match
expression is the value of the corresponding expression
. Patterns may be:
expression
, or a subtype of it.expressioni
).List(a, _, c)
, where the components are patterns.Some(x)
or None
.s: String
or m: Map[_, _]
. (For parameterized types such as List
and Map
, you cannot specify the types of their components.)if
followed by a Boolean expression), for example, n: Int if n > 0
.Cases are tried in the order in which they occur. When a case is selected, the code for that case, and only for that one case, is executed.
Scala does not have "statements," it has expressions, and every expression has a value. However, some expressions are executed purely for their side effects, and return the Unit
value, ()
, which is essentially meaningless. Such expressions are basically the same as statements in other languages.
Syntax | Example | Comments |
---|---|---|
{ expressions } |
{ temp = a; a = b; b = temp } |
A compound expression consists of one or more expressions enclosed in braces. The value of a compound expression is the value of the last expression evaluated within it; all other expressions are evaluated only for their side effects. The value of a compound expression may or may not be Unit. |
var name: type = value |
var abc = 5 |
Variables must be given initial values, except when within a class. |
var name: type |
var abc: Int |
Allowable within a class. |
val name: type = value |
val five = 5 |
val declares a constant, which must be given a value; explicit type information is allowed but redundant. A val name for an object is constant in the sense that the name cannot be reassigned; but the contents of the object may be modified. |
name = expression |
abc = 10 |
Values may be assigned only to var variables. |
name += expression |
abc += 1 |
All the various op= operators are allowed. ++ and -- do not exist in Scala. |
|
|
Zero or more else if clauses may be used, and the final else is optional. Braces can be used to group several expressions into a single compound expression. The value of the if is the value of the last expression evaluated within it (which may be Unit).If there is no final else , the type of the expression is Any , regardless of whatever value it may have. |
while (condition) statement |
|
Nothing unusual to note. However, Scala has no break or continue statements. |
do { statements } while (condition) |
|
Nothing unusual to note. However, Scala has no break or continue statements. |
for (variable <- list) statement |
|
The parenthesized expression may contain tests, assignments, and additional generators (variable <- list expressions). |
for (variable <- min to max) statement |
|
to is a method of RichInt . to is inclusive; until excludes max . |
for (variable <- min to max if condition) |
for (i <- 1 to 15 if i % 2 == 0) print(i) |
for comprehensions may have guards. |
|
|
Returns a value from a method. Use of |
Syntax:
override
] [access
] def methodName(arg: type, ..., arg: type) { body } // returns Unit
override
] [access
] def methodName(arg: type, ..., arg: type): returnType = { body }
access
may be private
, protected
, or public by default.
The return type of a method can usually be omitted; it must be declared:
return
statement.Any
.For reasons of clarity, it is usually best to declare the return type.
Braces around the body may be omitted if the body consists of a single expression, whose value is to be returned.
Methods may be overloaded, as in Java.
Inherited methods may be overridden, in which case the method definition must be preceded by the keyword override
.
The parameters of a method or function are treated as if they were defined by val
; they cannot be reassigned a new value.
Syntax: (arg1: Type1, ..., argN:TypeN) => expression
Within an enclosing argument list, the parentheses around the parameter list can usually be omitted.
Example: "aBcDeF".map(x => x toLower)
If each parameter is used only once, and the parameters appear in the
expression in the same order as in the parameter list, the parameter
list may be omitted, and underscores may be used in the expression.
Example: "aBcDeF".map(_ toLower)
Methods can often be passed as if they were functions, for example, "abc" map println
works. In other cases, the method name must be followed by an underscore to convert it to a "partial function," for example,
.
Methods are functions that belong to objects, and as such, can manipulate the fields of their object. Methods that do this should not be used as functions.
A script is a list of commands that can be interpreted directly by the Scala system. Scala wraps the script in a main
method and executes it immediately. The syntax for running a script is simply scala myScript.scala
.
A class describes objects, as in Java. A class may extend one other class, and it may include any number of traits. A class has a primary constructor, which is part of the class declaration itself.
class Foo { ... }
AnyRef
.class Foo(name: String) { constructor_body }
class Foo(a: Int, var b: Int, val c: Int) { ... }
a
has private getters and setters; var b
has public getters and setters; and val c
has a public getter. A setter method for a parameter v
has the name v_=
.def this(parameters1) { this(parameters2); ... }
this
; the first statement in the auxiliary constuctor must be a call to some other constructor.class Foo(a: Int, b: Int) extends Bar(a + b) { ... }
class Dog(name: String) extends Animal with Friend { ... }
with
. Any uninitialized var
s and val
s defined by the trait must be initialized by the class.In addition to creating objects from classes, you can declare objects. An object is declared like a class, but with the keyword object
instead of class
; also, an object cannot take parameters. Whereas a class
declaration describes a blueprint for objects, an object
declaration declares a single object.
A program, other than a script, must contain an object with a main(args: Array[String])
method.
When you define a class and create objects from it, you use the keyword new
:
scala> class Dog(name: String)
defined class Dog
scala> val a = new Dog("Fido")
a: Dog = Dog@a80370d
But when you create an object "literal", you don't use new
:
scala> val t = Tuple(1, "apple")
t: (Int, java.lang.String) = (1,apple)
scala> val l = List(1, "apple")
l: List[Any] = List(1, apple)
scala> val s = Set(1, "apple")
s: scala.collection.immutable.Set[Any] = Set(1, apple)
Traits are declared like classes, used like Java interfaces, and may contain fully-defined methods.
val data = readLine // Reads a single line (as a String) from the Console
val data = readLine(prompt) // Prints the prompt, then reads a line
Note: In the REPL, readLine
does not display the text being entered.
print(data) // Prints to the Console without a newline
println(data) // Prints to the Console with a newline
println // Just prints a newline
import scala.swing.FileChooser, java.io.File
val file = new File(fileName) // with a relative or absolute path
val chooser = new FileChooser
val response = chooser.showOpenDialog(null) // to read a file
val response = chooser.showSaveDialog(null) // to write a file
if (response == FileChooser.Result.Approve) { val file =
chooser.selectedFile }
import scala.io.Source, scala.io.Source._
process each character
val stream = Source.fromFile(file or fileName)
for (char <- stream) {}
for (lines <- stream.getLines) {
process each line }
val all = stream.getLines.toList // as a list of strings
val all = stream.getLines.mkString // as a single string
val all = stream.getLines.mkString("\n") // with newlines
stream.close
import java.io.PrintWriter
val stream = new PrintWriter(file or fileName)
stream.print(data)
stream.println(data)
stream.println
stream.close
Here is a template for a GUI program:
import scala.swing._
import event._
object SampleGUI extends SimpleSwingApplication {
def top = new MainFrame { // top is a required method
title = "A Sample Scala Swing GUI"
val label = new Label { text = "---------------" }
val button = new Button { text = "Click me" }
contents = new FlowPanel {
contents += label
contents += button
}
listenTo(button)
reactions += {
case ButtonClicked(button) =>
label.text = "You clicked!"
}
}
}
MainFrame
extends Frame with RootPanel with Publisher
. MainFrame
includes:
From Frame |
From RootPanel |
From Publisher |
|
---|---|---|---|
Values | listeners |
||
reactions |
|||
Methods | contents_=(c: Component): Unit |
contents : Seq[Component] (at most one component) |
listenTo (ps : Publisher*) : Unit |
title : java.lang.String |
contents_= (c : Component) : Unit |
deafTo (ps : Publisher*) : Unit |
|
menuBar_=(m: MenuBar): Unit |
font_=(f : java.awt.Font) : Unit |
Here are some Component
s:
Component type | Vals and vars | Methods |
---|---|---|
class Button(text0 : java.lang.String) |
listeners |
|
class TextField(text0 : java.lang.String, columns0 : Int) |
listeners |
|
class TextArea(text0 : java.lang.String, rows0 : Int, columns0 : Int) |
listeners |
text : java.lang.String |
class CheckBox(text : java.lang.String) |
listeners |
selected : Boolean |
class ButtonGroup(initialButtons : AbstractButton*) |
buttons : Set[AbstractButton] |
select (b : AbstractButton) : Unit |
class ComboBox[A](items : Seq[A]) |
listeners |
|
class MenuBar |
contents |
|
class Menu(title0 : java.lang.String) |
contents |
|
class MenuItem(title0 : java.lang.String) |
listeners |
|
Panel/Layout | Example |
---|---|
class BorderPanel |
|
class BoxPanel(orientation : Value) |
|
class FlowPanel(alignment : Value) |
|
class GridBagPanel |
|
class GridPanel(rows0 : Int, cols0 : Int) |
|
All Panels
have: contents
, listeners
, reactions
, contents
, tooltip_=
, listenTo
, deafTo
.
All LayoutContainer
s have add (comp : Component, c : Constraints) : Unit.